#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "t_types.h"
#include "common/lock.h"
#include "common/config.h"
#include "hal/hal_uart.h"
#include "pal/card_reader.h"
#include "userLog.h"
#include "console.h"

#define CARD_READER_CMD_FILE T_CONFIG_ROOT_PATH "/card_reader_cmd.ini"
#define GET_PROTOCOL_TOTAL_LEN(s32DataLen) (s32DataLen + sizeof(CARD_READER_HEAD_T) + 1)

typedef struct _PROTOCOL1_CONTEXT_T
{
	T_UINT8 au8RecvBuf[PROTOCOL1_RECV_BUF_LEN];
	T_UINT8 au8SendBuf[PROTOCOL1_SEND_BUF_LEN];
	T_UINT32 u32RecvPos;
	T_UINT32 u32SendPos;
	T_INT32 nRecvFlag;
	T_INT32 nPortNum;
	PROTOCOL_CR_READ_CARD_S stCardInfo;
	T_LOCK_HANDLE hRecvLock;
	T_UINT8 u8FrameIndex;					// ֡��ţ�00~07 ����ѭ��
	T_UINT8 u8LastCmd;						// PROTOCOL_CR_CMD_TYPE_E
	PROTOCOL_PROVINCE_CODE_E eProvinceCode; // ʡ���� 0x44�����㶫
} PROTOCOL1_CONTEXT_T;

/*********************�����******************************/
T_UINT8 au8Cmd60H[] = {0xFF, 0x05, 0x60, 0x07, 0x00, 0xA4, 0x00, 0x00, 0x02, 0x10, 0x01, 0x2A};
T_UINT8 au8Cmd70H[] = {0xFF, 0x04, 0x70, 0x05, 0xAA, 0xAA, 0xAA, 0x00, 0x44, 0x22};
T_UINT8 au8Cmd80H_old[] = {0xFF, 0x03, 0x80, 0x00, 0x7C};
T_UINT8 au8Cmd80H[] = {0xFF, 0x03, 0x80, 0x01, 0x01, 0x7C};

/*********************end �����******************************/

console_msg_declare(card_reader);

static PROTOCOL1_CONTEXT_T *pstContext = NULL;
static T_INT32 gss32UartChannelNum = 0;
//
static T_UINT8 _PROTOCOL1_CRC(T_UINT8 *pu8Data, T_INT32 nLen)
{
	int i;
	T_UINT8 u8CRC = 0;

	for (i = 0; i < nLen; i++)
	{
		u8CRC ^= pu8Data[i];
	}

	return u8CRC;
}
#define PROTOCOL_CR_GET_CRC(phead) _PROTOCOL1_CRC(&phead->u8HeadFlag, phead->u8DataLen + sizeof(CARD_READER_HEAD_T))

static T_UINT8 _PROTOCOL_CR_GetIndex(void)
{
	if (pstContext)
	{
		pstContext->u8FrameIndex++;
		pstContext->u8FrameIndex = pstContext->u8FrameIndex > 7 ? 0 : pstContext->u8FrameIndex;
		return pstContext->u8FrameIndex;
	}
	return 0;
}

static T_INT32 _PROTOCOL1_FrameIndexCompare(T_UINT8 u8Index1, T_UINT8 u8Index2)
{
	if (u8Index1 == u8Index2)
	{
		return 1;
	}
	return 0;
}
static T_INT32 _PROTOCOL1_Send(T_UINT8 *pu8Buf, T_INT32 nLen)
{
	if (pstContext == NULL)
	{
		LOG_ERROR("context not init");
		return -1;
	}

	return HAL_UART_Send(gss32UartChannelNum, pu8Buf, nLen);
}

static T_INT32 _PROTOCOL1_Recv(T_UINT8 *pu8Buf, T_INT32 nBufLen)
{
	if (pstContext == NULL)
	{
		LOG_ERROR("context not init");
		return -1;
	}
	return HAL_UART_Recv(gss32UartChannelNum, pu8Buf, nBufLen);
}

/**
* return -1:error;0:no one frame in buf; 1:get one frame in buf
*/
static T_INT32 _PROTOCOL_CR_CheckFrame(T_UINT8 *pu8Buff, T_INT32 s32BuffLen)
{
	CARD_READER_HEAD_T *pstHead = NULL;
	T_INT32 s32Offset = 0;
	T_INT32 s32TotalLen = 0;
	T_UINT8 u8Crc = 0;
	T_UINT8 *pu8Crc = NULL;

	if (!pu8Buff || s32BuffLen <= 0)
	{
		return -1;
	}

	// have head data enough?
	if (s32BuffLen < (sizeof(CARD_READER_HEAD_T) + 1))
	{
		return 0;
	}
	pstHead = (CARD_READER_HEAD_T *)pu8Buff;
	s32TotalLen = GET_PROTOCOL_TOTAL_LEN(pstHead->u8DataLen);
	// have total one frame data enough?
	if (s32BuffLen < s32TotalLen)
	{
		return 0;
	}
	// crc correct?
	u8Crc = PROTOCOL_CR_GET_CRC(pstHead);
	pu8Crc = (T_UINT8 *)(pstHead->au8Data + pstHead->u8DataLen);
	if (u8Crc != *pu8Crc)
	{
		return 0;
	}
	return 1;
}

static T_INT32 _PROTOCOL_CR_HandleCmd(CARD_READER_HEAD_T *pstHead)
{
	if (!pstHead || !pstContext)
	{
		return -1;
	}
	switch (pstHead->u8Cmd)
	{
	case PROTOCOL_CR_CMD_00H_RESPONSE_OK:
	{
		LOG_INFO("response ok");
		switch (pstContext->u8LastCmd)
		{
		case PROTOCOL_CR_CMD_80H_READ_CARD:
		{
			PROTOCOL_CR_READ_CARD_S *pData = (PROTOCOL_CR_READ_CARD_S *)pstHead->au8Data;
			pstContext->stCardInfo.u32CardNum = COMM_NetToHostL(pData->u32CardNum);
			pstContext->stCardInfo.u8CardPos = pData->u8CardPos;
			pstContext->stCardInfo.u8CardType = pData->u8CardType;
			LOG_INFO("card num:0x%x", pstContext->stCardInfo.u32CardNum);
			LOG_INFO("card type:0x%x, pos:0x%x", pData->u8CardType, pData->u8CardPos);
			break;
		}
		case PROTOCOL_CR_CMD_70H_DEV_INIT_CONN:
		{
			LOG_INFO("70H return:");
			hzlog_info(userLog, (char *)(&pstHead->u8HeadFlag), GET_PROTOCOL_TOTAL_LEN(pstHead->u8DataLen));
			break;
		}
		default:
		{
			//LOG_ERROR("unknow last cmd:0x%x", pstContext->u8LastCmd);
			LOG_WARN("cmd:0x%x not handle return msg", pstContext->u8LastCmd);
			//return -1;
		}
		}
		break;
	}
	case PROTOCOL_CR_CMD_80H_ERR:
	{
		LOG_ERROR("read card error");
		break;
	}
	}

	return 0;
}
static T_INT32 _PROTOCOL_CR_UartCb(void *pvParam)
{
	HAL_UART_CB_PARAM_S *pstCbParam = (HAL_UART_CB_PARAM_S *)pvParam;
	T_UINT8 *pu8Buff = NULL;
	T_INT32 s32DataLen = 0;
	T_INT32 s32Ret = 0;
	CARD_READER_HEAD_T *pstHead;

	if (!pstContext)
	{
		return -1;
	}

	if (!pstCbParam)
	{
		LOG_ERROR("invalid arguments");
		return -1;
	}

	memcpy((void *)(pstContext->au8RecvBuf + pstContext->u32RecvPos), (void *)(pstCbParam->pu8Buff), pstCbParam->s32Len);
	pstContext->u32RecvPos += pstCbParam->s32Len;

	s32DataLen = pstContext->u32RecvPos;
	pu8Buff = pstContext->au8RecvBuf;
	LOG_INFO("cb:");
	hzlog_info(userLog, pu8Buff, s32DataLen);
	while (s32DataLen >= (sizeof(CARD_READER_HEAD_T) + 1))
	{
		s32Ret = _PROTOCOL_CR_CheckFrame(pu8Buff, s32DataLen);
		// get one frame
		if (s32Ret == 1)
		{
			pstHead = (CARD_READER_HEAD_T *)pu8Buff;
			_PROTOCOL_CR_HandleCmd(pstHead);
			LOG_INFO("pstHead->u8DataLen:%d", pstHead->u8DataLen);
			pu8Buff += GET_PROTOCOL_TOTAL_LEN(pstHead->u8DataLen);
			s32DataLen -= GET_PROTOCOL_TOTAL_LEN(pstHead->u8DataLen);
			continue;
		}
		pu8Buff++;
		s32DataLen--;
	}

	// move remain data
	if (s32DataLen > 0 && pu8Buff != pstContext->au8RecvBuf)
	{
		memcpy(pstContext->au8RecvBuf, pu8Buff, s32DataLen);
		pstContext->u32RecvPos = s32DataLen;
	}
	else if (s32DataLen == 0)
	{
		pstContext->u32RecvPos = 0;
	}

	return 0;
}

static T_INT32 _PROTOCOL1_SendCmd(T_UINT8 *pu8CmdBuf, T_INT32 s32Len)
{
	if (!pu8CmdBuf || s32Len <= 0)
	{
		LOG_ERROR("invalid argments");
		return -1;
	}

	//frame index
	pu8CmdBuf[1] = _PROTOCOL_CR_GetIndex();
	//crc
	pu8CmdBuf[s32Len - 1] = _PROTOCOL1_CRC(pu8CmdBuf, s32Len - 1);
	//send
	pstContext->u8LastCmd = pu8CmdBuf[2];
	if (_PROTOCOL1_Send(pu8CmdBuf, s32Len) < 0)
	{
		LOG_ERROR("send cmd error");
		return -1;
	}
	return 0;
}

/*s32ProvinceCode. 0x44 for GuangDong*/
T_INT32 PROTOCOL_CR_70H_InitConnect(T_INT32 s32ProvinceCode)
{
	T_UINT8 u8CRC = 0;
	CARD_READER_HEAD_T *pstHead = NULL;
	CARD_READER_HEAD_T *pstResHead = NULL;
	T_INT32 s32Ret = 0;
	T_UINT8 au8RecvBuf[64];

	if (!pstContext)
	{
		return -1;
	}

	pstHead = (CARD_READER_HEAD_T *)pstContext->au8SendBuf;

	pstHead->u8HeadFlag = 0xFF;
	pstHead->u8FrameIndex = _PROTOCOL_CR_GetIndex();
	pstHead->u8Cmd = 0x70;
	pstHead->u8DataLen = 0x05;
	pstHead->au8Data[0] = 0xAA;
	pstHead->au8Data[1] = 0xAA;
	pstHead->au8Data[2] = 0xAA;
	pstHead->au8Data[3] = s32ProvinceCode;

	pstHead->au8Data[4] = PROTOCOL_CR_GET_CRC(pstHead);

	s32Ret = _PROTOCOL1_Send(&pstHead->u8HeadFlag, GET_PROTOCOL_TOTAL_LEN(pstHead->u8DataLen));
	if (s32Ret <= 0)
	{
		LOG_ERROR("send 70H_InitConnect error");
		return -1;
	}

	memset(au8RecvBuf, 0, sizeof(au8RecvBuf));
	s32Ret = _PROTOCOL1_Recv(au8RecvBuf, sizeof(au8RecvBuf));
	if (s32Ret <= 0)
	{
		LOG_ERROR("_PROTOCOL1_Recv() error");
		return -1;
	}
	LOG_INFO("init cr return len:%d", s32Ret);
	hzlog_info(userLog, au8RecvBuf, s32Ret);

	pstContext->u8LastCmd = pstHead->u8Cmd;

	return 0;
}

T_INT32 PROTOCOL_CR_80H_ReadCard(void)
{
	CARD_READER_HEAD_T *pstHead = NULL;
	T_INT32 s32Ret = 0;
	T_UINT8 au8RecvBuf[64];

	if (!pstContext)
	{
		return -1;
	}

	s32Ret = _PROTOCOL1_SendCmd(au8Cmd80H, sizeof(au8Cmd80H));
	if (s32Ret < 0)
	{
		LOG_ERROR("send cmd error");
		return -1;
	}

	memset(au8RecvBuf, 0, sizeof(au8RecvBuf));
	s32Ret = _PROTOCOL1_Recv(au8RecvBuf, sizeof(au8RecvBuf));
	if (s32Ret <= 0)
	{
		LOG_ERROR("_PROTOCOL1_Recv() error");
		return -1;
	}
	LOG_INFO("read card return len:%d", s32Ret);
	hzlog_info(userLog, au8RecvBuf, s32Ret);

	return 0;
}

/* return: -2:no card; -1:error; 0:ok */
T_INT32 PROTOCOL_CR_GetCarNum(PROTOCOL_CR_RESULT_S *pstCarNumResult)
{

	CARD_READER_HEAD_T *pstHead = NULL;
	T_INT32 s32Ret = 0;
	T_UINT8 au8RecvBuf[64];

	if (!pstContext)
	{
		return -1;
	}

	s32Ret = _PROTOCOL1_SendCmd(au8Cmd80H, sizeof(au8Cmd80H));
	if (s32Ret < 0)
	{
		LOG_ERROR("send cmd error");
		return -1;
	}

	memset(au8RecvBuf, 0, sizeof(au8RecvBuf));
	s32Ret = _PROTOCOL1_Recv(au8RecvBuf, sizeof(au8RecvBuf));
	if (s32Ret <= 0)
	{
		LOG_ERROR("_PROTOCOL1_Recv() error");
		return -1;
	}
	LOG_INFO("read card return len:%d", s32Ret);
	hzlog_info(userLog, au8RecvBuf, s32Ret);

	pstHead = (CARD_READER_HEAD_T *)au8RecvBuf;
	if (pstHead->u8Cmd == PROTOCOL_CR_CMD_80H_ERR)
	{
		LOG_ERROR("read car error");
		return -2;
	}
	else if (pstHead->u8Cmd == PROTOCOL_CR_CMD_80H_READ_CARD)
	{
		memcpy(pstCarNumResult->au8CarNum, pstHead->au8Data, PROTOCOL_CR_CAR_NUM_RESULT_LEN);
		return 0;
	}
	else
	{
		LOG_ERROR("read card error");
		return -1;
	}

	return 0;
}

static int _CMD_ProtocolCRGetCardNum(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	//_PROTOCOL1_SendCmd(au8Cmd80H_old, sizeof(au8Cmd80H_old));
	int nCount = 10;
	char abyHelp[] = "./cmd count";

	if (pParam->nArgsNum != 1)
	{
		sprintf(pAck, "%s\n", abyHelp);
		return 0;
	}

	nCount = atoi(pParam->abyArgs[0]);
	while (nCount--)
	{
		PROTOCOL_CR_80H_ReadCard();
		uv_sleep(500 * 1000);
	}
	sprintf(pAck, "ok\n");
	return 0;
}
static int _CMD_ProtocolCRInitConn(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	//_PROTOCOL1_SendCmd(au8Cmd70H, sizeof(au8Cmd70H));
	PROTOCOL_CR_70H_InitConnect(0x44);
	sprintf(pAck, "ok\n");
	return 0;
}

static int _CMD_ProtocolCRSendCmd(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	char abyHelp[] = "./cmd hexData. ex. 4a4b\n";
	int s32Len = 0;
	char data[32];
	int nParam = 0;

	if (pParam->nArgsNum != 1)
	{
		sprintf(pAck, "%s", abyHelp);
		return 0;
	}

	s32Len = strlen(pParam->abyArgs[0]);
	COMM_HexStrToByte(pParam->abyArgs[0], data, s32Len);
	LOG_INFO("input data len:%d, data:", s32Len);
	hzlog_info(userLog, data, s32Len / 2);

	_PROTOCOL1_SendCmd(data, s32Len / 2);

	sprintf(pAck, "ok\n");
	return 0;
}

T_INT32 PROTOCOL_CR_LoadAndSendCmd(void)
{
	char line[64];
	T_UINT8 cmd[32];
	FILE *fp = NULL;
	int s32Len = 0;

	fp = fopen(CARD_READER_CMD_FILE, "r");
	if (!fp)
	{
		LOG_ERROR("can not open file:%s", CARD_READER_CMD_FILE);
		return -1;
	}

	while (fgets(line, sizeof(line), fp))
	{
		s32Len = strlen(line);
		COMM_HexStrToByte(line, cmd, s32Len);
		_PROTOCOL1_SendCmd(cmd, s32Len / 2);
		uv_sleep(100 * 1000);
	}
	return 0;
}

T_INT32 PROTOCOL_CR_Init(void)
{
	T_INT32 s32Ret = 0;

	pstContext = (PROTOCOL1_CONTEXT_T *)malloc(sizeof(PROTOCOL1_CONTEXT_T));
	if (!pstContext)
	{
		LOG_ERROR("can not malloc for protocol cr context");
		return -1;
	}
	memset((void *)pstContext, 0, sizeof(PROTOCOL1_CONTEXT_T));

	//init context
	pstContext->eProvinceCode = PROTOCOL_PROVINCE_CODE_GUANGDONG;

	s32Ret = HAL_UART_Probe(HAL_UART_APP_NAME_CR);
	if (s32Ret < 0)
	{
		LOG_ERROR("HAL_UART_Probe() error");
		return -1;
	}
	gss32UartChannelNum = s32Ret;
	LOG_INFO("CardRead Uart in chn:%d", gss32UartChannelNum);

#if 0
	s32Ret = HAL_UART_RegisterRecvCallback(gss32UartChannelNum, _PROTOCOL_CR_UartCb);
	if(s32Ret < 0)
	{
		LOG_ERROR("HAL_UART_RegisterRecvCallback() _PROTOCOL_CR_UartCb() error");
		return -1;
	}
#endif

	Console_CmdAdd("t-pal-cr-getcardnum", _CMD_ProtocolCRGetCardNum);
	Console_CmdAdd("t-pal-cr-initconnect", _CMD_ProtocolCRInitConn);
	Console_CmdAdd("t-pal-cr-sendcmd", _CMD_ProtocolCRSendCmd);

	return 0;
}

T_INT32 PROTOCOL_CR_DeInit(void)
{
	HAL_UART_unRegisterRecvCallback(gss32UartChannelNum);

	if (pstContext)
	{
		free((void *)pstContext);
	}

	return 0;
}
